#include <glib.h>
#include <stdlib.h>
#include <assert.h>
#include "command.h"

struct scommand_s
{
	bstring input_redir;
	bstring output_redir;
	GList * lista_com; /*lista con comandos y argumentos */
};

#define BASE_ALLOC 16

scommand *scommand_new (void)
{
	scommand * result = NULL;

	result = calloc (1, sizeof (*result));
	assert (result != NULL);
	/* inicializamos los campos de la estructura */
	result -> input_redir = NULL;
	result -> output_redir = NULL;
	result -> lista_com = NULL;

	return result;
}

void scommand_destroy (scommand *self)
{
	unsigned int cant=0, tam=0;
	bstring aux=NULL;

	assert (self != NULL);

	if (self -> input_redir != NULL)
		bdestroy (self -> input_redir);
	if (self -> output_redir != NULL)
		bdestroy (self -> output_redir);

	tam=scommand_length (self);

	if (self -> lista_com != NULL)
	{
		while(cant < tam)
		{
			aux = g_list_nth_data (self -> lista_com, cant);
			bdestroy(aux);
			cant++;
		}
		g_list_free (self -> lista_com);
	}

	free(self);
	self = NULL;
}

void scommand_push_back (scommand *self, bstring argument)
{
	assert (self != NULL && argument != NULL);
	self -> lista_com = g_list_append (self -> lista_com, argument);
	assert ( !scommand_is_empty(self));
}

void scommand_pop_front (scommand *self)
{
	bstring aux=NULL;
	assert (self != NULL && (!scommand_is_empty(self)));
	aux = g_list_nth_data(self->lista_com, 0);
	self -> lista_com = g_list_remove (self -> lista_com, aux);
	/*bdestroy (aux);*/
}

void scommand_set_redir_in (scommand *self, bstring filename)
{
	assert (self != NULL);
	if (self -> input_redir != NULL)
		bdestroy (self -> input_redir);
	self -> input_redir = filename;
}

void scommand_set_redir_out (scommand *self, bstring filename)
{
	assert (self != NULL);
	if (self -> output_redir != NULL)
		bdestroy (self -> output_redir);
	self -> output_redir = filename;
}

bool scommand_is_empty (const scommand *self)
{
	bool b;

	assert (self != NULL);
	b = g_list_length (self -> lista_com) == 0;
	return b;
}

unsigned int scommand_length (const scommand *self)
{
	unsigned int len;

	assert (self != NULL);
	len = g_list_length (self -> lista_com);
	return len;
}

const_bstring scommand_front (const scommand *self)
{
	bstring head = NULL;
	assert (self != NULL && (!scommand_is_empty(self)));
	
	head = (bstring) g_list_nth_data (self -> lista_com, 0);

	assert (head != NULL);
	return head;
}

const_bstring scommand_get_redir_in (const scommand *self)
{
	bstring input = NULL;
	assert (self != NULL);

	input = self -> input_redir;
	return input;
}

const_bstring scommand_get_redir_out (const scommand *self)
{
	bstring output = NULL;
	assert (self != NULL);

	output = self -> output_redir;
	return output;
}

bstring scommand_to_string (const scommand *self){
	unsigned int i = 0;
	bstring result = NULL;
	assert (self!=NULL);
	
	result = bfromcstralloc(BASE_ALLOC,"");
	
	while(i < scommand_length(self)){
		bconcat(result, g_list_nth_data(self->lista_com, i));
		bcatcstr(result," ");
		i = i+1;
	}
	
	/*obtengo los redirectores*/
	if(scommand_get_redir_in(self) != NULL){
		bcatcstr(result,"< ");
		bconcat(result,self->input_redir);
	}
	
	if(scommand_get_redir_out(self) != NULL){
		bcatcstr(result," > ");
		bconcat(result,self->output_redir);
	}
	
	assert (scommand_is_empty(self) || scommand_get_redir_in(self)==NULL || 
		scommand_get_redir_in(self)==NULL || blength(result) > 0);
	
	return result;	
	
}

struct pipeline_s{
	GList * scomand;
	bool terminador;
	
};

pipeline *pipeline_new (void){
	pipeline * comand = NULL;
	comand = calloc(1, sizeof (*comand));

	assert(comand != NULL);
	
	comand->scomand=NULL;
	comand->terminador=true;
	
	return (comand);
	
}

void pipeline_destroy (pipeline *self){
	unsigned int tam=0, cant=0;
	scommand *aux=NULL;
	assert (self != NULL);

	if (self -> scomand != NULL){  /*si no es null el free va a tirar error*/
		tam=pipeline_length (self);
		if (self -> scomand){
			while(cant < tam){
				aux=g_list_nth_data (self -> scomand, cant);
				scommand_destroy (aux);
				cant++;
			}
		}
		g_list_free (self->scomand);
	}
	
	free (self);
	self = NULL;
}


void pipeline_push_back (pipeline *self, scommand *sc)
{
	assert (self != NULL && sc != NULL);
	self->scomand = g_list_append (self->scomand, sc);
}


void pipeline_pop_front (pipeline *self){
		scommand *head = NULL;
		
		assert (self!=NULL && !pipeline_is_empty(self));
		
		head = g_list_nth_data (self->scomand, 0);
		self->scomand = g_list_remove (self->scomand, head);
		/*scommand_destroy (head);*/
}

void pipeline_set_wait (pipeline *self, const bool w){
	assert(self != NULL);

	self->terminador = w;
}
  
bool pipeline_is_empty (const pipeline *self){
	bool vacio=false;

	assert (self != NULL);
	vacio=(pipeline_length (self) == 0);

	return vacio;
}

unsigned int pipeline_length (const pipeline *self){
	unsigned int n;

	assert (self != NULL);

	n=g_list_length (self->scomand);

	return n;
}

scommand *pipeline_front (const pipeline *self)
{
	scommand * result = NULL;
	assert (self!=NULL && !pipeline_is_empty(self));

	result = (scommand *) g_list_nth_data (self -> scomand, 0);
	return result;
}


bool pipeline_get_wait (const pipeline *self)
{
	assert (self != NULL);
	return (self->terminador);
}


bstring pipeline_to_string(const pipeline *self)
{
	unsigned int i = 0;
	bstring result = NULL, comand = NULL;
	scommand *aux = NULL;
	assert(self != NULL);

	result = bfromcstralloc (BASE_ALLOC, "");
	
	if (!(pipeline_is_empty(self)))
	{
		while (i < pipeline_length(self))
		{
			aux = g_list_nth_data (self->scomand, i); /* es el i-esimo scomand*/
			comand = scommand_to_string (aux); /*le hacemos pretty-printing */
			bconcat (result, comand); /* se lo agregamos al resultado parcial */
			if ( ((pipeline_length (self)) == 2) && (i == 0))
				bcatcstr (result, " | ");

			i = i+1;
		}

		if (!pipeline_get_wait (self))
			bcatcstr (result, " &");
	
		bdestroy(comand);
	}

	return result;
}
